﻿
#include <assert.h>

#include "ClientSocket.h"
#include "MemBlockPool2.h"

#include "CoreHeadForClient.h"
#include "GnsClientFrame.h"

#include "ILogic.h"
#include "IDataRepository.h"

ClientSocket::ClientSocket()
{
	memset(_serverIp, 0, sizeof(0));
	sprintf(_serverIp, "127.0.0.1");

	m_bReconnect = false;
	m_bStart = false;

	_sendHeartbeatElapse = 15*1000;			// 发送心跳的间隔
}

ClientSocket::~ClientSocket()
{

}

// 设置客户端 Socket 的信息
void ClientSocket::setClientInfo(int iClientId, const char* szServerIp, int iPort, int iMsgBlockSize, int bManualConnect)
{
	_clientId = iClientId;
	sprintf( _serverIp, "%s", szServerIp);
	_port = iPort;

	_msgBlockSize = iMsgBlockSize;				// 消息块的长度
	_manualConnect = bManualConnect;		

	if (!m_pStoreBox)
	{
		m_pStoreBox = new CMemBlock2("ClientSocket::set_msg_block_size", 3*_msgBlockSize + 32);	// 加 32 是为了防止边界溢出
	}
}

/* --------------------------------------------------------------------------
函数说明：设置重连信息
传入参数：
	iAutoReConnectTime		断线后自动重连的间隔时间，单位ms，即断线后多少ms才能再次重连
	iRetryConnectMaxCount				重连的次数
-------------------------------------------------------------------------- */
void ClientSocket::setReconnectInfo(int iAutoReConnectTime, int iRetryConnectMaxCount)
{			
	_autoReConnectTime = iAutoReConnectTime;	

	if (iRetryConnectMaxCount <= RETRY_CONNECT_MAX_COUNT)
		_retryConnectMaxCount = iRetryConnectMaxCount;
	else
		_retryConnectMaxCount = RETRY_CONNECT_MAX_COUNT;
}

bool ClientSocket::init()
{

    return true;
}

/* --------------------------------------------------------------------------
函数说明：启动
传入参数：
	bAutoReConnect  断线后自动重连
返回值：
	成功则返回 0，失败返回错误码
-------------------------------------------------------------------------- */
int ClientSocket::connectSocket(bool bAutoReConnect)
{
	_autoReConnect = bAutoReConnect;
	m_bStart = true;
	_curRetryCount = 0;					// 清零：当前重连次数

	if (!_bCoreWorkerThread)
	{
		// 内核消息的工人线程
		_coreWorkerThread = std::thread(&ClientSocket::threadCoreWork, this);
		_bCoreWorkerThread = true;
	}

	if (!_bframeFunThread)
	{
		_frameFunThread = std::thread(&ClientSocket::timerFrameFun, this);
		_bframeFunThread = true;
	}

	std::lock_guard<std::mutex> guard(_mutexCoreMsg);		// 加锁
	_dequeCoreMsg.push_back(CORE_SOCKET_CONNECT);	// 注意：是发连接消息，不是断线重连消息

	return 0;
}

void ClientSocket::closeSocket(bool bAutoReConnect )
{
	_autoReConnect = bAutoReConnect;
	_socket.Close();
	_curRetryCount = 0;					// 清零：当前重连次数
}

int ClientSocket::stop()
{
	m_bStart = false;
	_socket.Close();

	if (_frameFunThread.joinable())
	{
		_frameFunThread.join();
	}

	// 等待接收线程结束
	if (_recvThread.joinable())
	{
		_recvThread.join();
	}

	// 等待工人线程结束
	if (_logicWorkerThread.joinable())
	{
		_logicWorkerThread.join();
	}

	// 等待发送线程结束
	if (_sendThread.joinable())
	{
		_sendThread.join();
	}

	if (_coreWorkerThread.joinable())
	{
		_coreWorkerThread.join();
	}

	return 0;
}

// 获得有效性
bool ClientSocket::isValid()
{
	if (m_bStart.load() && m_bReconnect.load())
		return true;
	else
		return false;
}

// 模拟框架功能的定时器
void ClientSocket::timerFrameFun()
{
	const int iSleepElapse = 1000;	// ms

	const int iMaxSecond = 30;	// 最大秒钟
	static int iSecond = 0;				// 当前秒钟

	while (m_bStart.load())
	{
		//--- 心跳 -------------------------
		if (iSecond < iMaxSecond)  // 还没到达指定的数量
		{
			iSecond++;
		}
		else  // 到达指定的数量
		{
			iSecond = 0;
			sendHeartbeatPackeg();		// 发送心跳包
		}

		std::this_thread::sleep_for(std::chrono::milliseconds(iSleepElapse));		// 毫秒级睡眠
	}
}

// 发送心跳包
int ClientSocket::sendHeartbeatPackeg()
{
	if (!isValid())
		return 10;

	CMemBlockPool2* _pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;

	int iMainCode = 0;
	int iSubCode = 0;
	CMemBlock2* pMemBlock = NULL;	// 消息块出池，记得归池

	// 出池一个携带指定内容的消息块
	int iRes = _pMsgPool->retrieve_with_content_no_channel("ClientSocket::sendHeartbeatPackeg", pMemBlock, iMainCode, iSubCode, NULL, 0);

	CORE_MSG_HEAD* pMsgHead = NULL;
	_pMsgPool->get_core_msg_head(pMemBlock, pMsgHead);
	if (!pMsgHead)
		return 10;  // 消息头出错了

	pMsgHead->set_heartbeat();	// 设置成心跳包

	// 发送
	sendMsg(pMemBlock);

	return 0;
}

// 连接服务端
bool ClientSocket::connectServer()
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	ILogic*	pLogic = pGnsClientFrame->_pLogic;

	std::string strErr;

	_socket.Init();
	_socket.Create(AF_INET, SOCK_STREAM, 0);

	m_bReconnect = _socket.Connect(_serverIp, _port);

	if (m_bReconnect.load())  // 连接成功
	{
		//pGnsClientFrame->OnConnect(_clientId, m_bReconnect.load(), _curRetryCount, strErr);
		pLogic->OnConnect(_clientId, m_bReconnect.load(), _curRetryCount, strErr);

		_curRetryCount = 0;	// 当前重连次数
	}
	else  // 连接失败
	{
		//pGnsClientFrame->OnConnect(_clientId, m_bReconnect.load(), _curRetryCount, strErr);
		pLogic->OnConnect(_clientId, m_bReconnect.load(), _curRetryCount, strErr);

		_curRetryCount++;	// 当前重连次数

		std::lock_guard<std::mutex> guard(_mutexCoreMsg);		// 加锁
		_dequeCoreMsg.push_back(CORE_SOCKET_CONNECT);	// 注意：是发连接消息，不是断线重连消息
	}

	return m_bReconnect.load();
}

// 断线重连
bool ClientSocket::reconnectAfterOffline()
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	ILogic*	pLogic = pGnsClientFrame->_pLogic;

	std::string strErr;

	_socket.Init();
	_socket.Create(AF_INET, SOCK_STREAM, 0);

	m_bReconnect = _socket.Connect(_serverIp, _port);

	if (m_bReconnect.load())  // 连接成功
	{
		//pGnsClientFrame->OnReconnectAfterOffline(_clientId, m_bReconnect.load(), _curRetryCount, strErr);
		pLogic->OnReconnectAfterOffline(_clientId, m_bReconnect.load(), _curRetryCount, strErr);

		_curRetryCount = 0;	// 当前重连次数
	}
	else  // 连接失败
	{
		//pGnsClientFrame->OnReconnectAfterOffline(_clientId, m_bReconnect.load(), _curRetryCount, strErr);
		pLogic->OnReconnectAfterOffline(_clientId, m_bReconnect.load(), _curRetryCount, strErr);

		_curRetryCount++;	// 当前重连次数

		std::lock_guard<std::mutex> guard(_mutexCoreMsg);		// 加锁
		_dequeCoreMsg.push_back(CORE_SOCKET_RECONNECT);	// 注意：是发断线重连消息，不是 连接消息
	}

	return m_bReconnect.load();
}

void ClientSocket::threadRecvData()
{
	while (isValid())
	{
		memset(recvBuf, 0, sizeof(recvBuf));

		// If no error occurs, recv returns the number of bytes received.If the connection has been gracefully closed, 
		// he return value is zero.Otherwise, a value of SOCKET_ERROR is returned, 
		// and a specific error code can be retrieved by calling WSAGetLastError.
        int iRecvSize = _socket.Recv(recvBuf, MAX_LEN, 0);

		if (iRecvSize <= 0)   // 这是 socket 中断时发出的消息
		{
			m_bReconnect = false;

			std::lock_guard<std::mutex> guard(_mutexCoreMsg);		// 加锁
			_dequeCoreMsg.push_back(CORE_SOCKET_CLOSE);
			
			log("threadRecvData CORE_SOCKET_CLOSE");

            break;
        }

		// 在这里要将消息打成“原子包“
		makeAtomMsgBlock(recvBuf, iRecvSize);

		// 将原子包打入“工人线程”的消息队列，由“工人线程”进行消化

    }
}

// 工人线程，专门处理 _dequeRecvMsg 消息
void ClientSocket::threadLogicWork()
{
	while (m_bStart.load())
	{
		// 必需放在本段的第一行，以防 continue 跳过
		// 暂时用睡眠跑着，以后要换成消息事件 ???
		std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_SLEEP_ELAPSE));

		std::deque<CMemBlock2*>  deqLogicWork;	// 逻辑消息的工作队列

		{
				std::lock_guard<std::mutex> guard(_mutexRecvMsg);	// 加锁

				int iSize =  _dequeRecvMsg.size();
				while (_dequeRecvMsg.size() > 0)
				{
					CMemBlock2* pMemBlock = _dequeRecvMsg.front();

					// 从容器中将该元素移除，但该元素要传出并使用，所以不能 delete
					_dequeRecvMsg.pop_front();

					if (pMemBlock)
					{
						// 将消息加入逻辑消息的工作队列
						deqLogicWork.push_back(pMemBlock);
					}
				}
		}	// 解锁，守护的析构会解除 _mutexRecvMsg 锁定

		//---处理 逻辑消息的工作队列 -----------------------------
		GnsClientFrame*		pGnsClientFrame = GnsClientFrame::getInstance();
		CMemBlockPool2*		pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;
		ILogic*	pLogic = pGnsClientFrame->_pLogic;

		int  iSize = deqLogicWork.size();

		while (deqLogicWork.size() > 0)
		{
			CMemBlock2* pMemBlock = deqLogicWork.front();

			// 从容器中将该元素移除，但该元素要传出并使用，所以不能 delete
			deqLogicWork.pop_front();

			int		iMainCode = 0;
			int		iSubCode = 0;
			char*	pData;
			int		iDataLen;
			pMsgPool->get_core_msg_data(pMemBlock, &iMainCode, &iSubCode, pData, &iDataLen);

			//pGnsClientFrame->OnLogicMsg(_clientId, iMainCode, iSubCode, pData, iDataLen);
			pLogic->OnLogicMsg(_clientId, iMainCode, iSubCode, pData, iDataLen);

			// 归池
			pMsgPool->recycle(pMemBlock, "ClientSocket::threadLogicWork");

			int iTmp = 0;
		}
	}
}

// 发送线程
void ClientSocket::threadSendData()
{
	while (m_bStart.load())  // 要保证断线时不退出线程
	{
		// 必需放在本段的第一行，以防 continue 跳过
		// 暂时用睡眠跑着，以后要换成消息事件 ???
		std::this_thread::sleep_for(std::chrono::milliseconds(_sendThreadSleep));

		std::deque<CMemBlock2*>  deqWillSend;	// 逻辑消息的工作队列

		if (m_bReconnect.load())  // 这一点很重要
		{
			std::lock_guard<std::mutex> guard(_mutexSendMsg);	// 加锁

			int iSize = _dequeSendMsg.size();	// 测试用
			int iWillSendLen = 0;							// 将要发送的长度

			while (_dequeSendMsg.size() > 0)
			{
				CMemBlock2* pMemBlock = _dequeSendMsg.front();

				if (!pMemBlock)	// 无效的消息
				{
					_dequeSendMsg.pop_front();
					continue;
				}

				iWillSendLen += pMemBlock->length();

				if (iWillSendLen < _sendMaxByteInOneCycle)	// 在“一个发送周期最大的发送长度”范围之内
				{
					// 从容器中将该元素移除，但该元素要传出并使用，所以不能 delete
					_dequeSendMsg.pop_front();

					if (pMemBlock)
					{
						// 将消息加入逻辑消息的工作队列
						deqWillSend.push_back(pMemBlock);
					}
				}
				else
				{
					break;		// 这一条消息加入就会超长，将放在下一个周期发送
				}
			}
		}	// 解锁，守护的析构会解除 _mutexRecvMsg 锁定

		//---处理 逻辑消息的工作队列 -----------------------------
		GnsClientFrame*			pGnsClientFrame = GnsClientFrame::getInstance();
		CMemBlockPool2*		pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;

		int  iSize = deqWillSend.size();

		while (deqWillSend.size() > 0)
		{
			CMemBlock2* pMemBlock = deqWillSend.front();

			// 从容器中将该元素移除，但该元素要传出并使用，所以不能 delete
			deqWillSend.pop_front();

			realSendMsg(pMemBlock);
		}
	}
}

// 内核消息的工人线程
void ClientSocket::threadCoreWork()
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	ILogic*	pLogic = pGnsClientFrame->_pLogic;

	while (m_bStart.load())
	{
		// 必需放在本段的第一行，以防 continue 跳过
		// 暂时用睡眠跑着，以后要换成消息事件 ???
		std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_SLEEP_ELAPSE));

		CORE_SOCKET_MSG tmp = CORE_SOCKET_INVALID;

		{
			std::lock_guard<std::mutex> guard(_mutexCoreMsg);	// 加锁

			if (_dequeCoreMsg.size() > 0)
			{
				tmp = _dequeCoreMsg.front();
				_dequeCoreMsg.pop_front();
			}
		}	// 解锁，守护的析构会解除

		if (CORE_SOCKET_INVALID == tmp)
		{
			continue;
		}
		else if (CORE_SOCKET_CONNECT == tmp)	// 连接
		{
			// 非连接 &&  断线后自动重连  &&  当前重连次数未超限
			if (!m_bReconnect.load() &&  _autoReConnect && _curRetryCount<=_retryConnectMaxCount)		// 断线后自动重连
			{
				if (connectServer())
				{
					if (!_bRecvThread)
					{
						// 接收线程
						_recvThread = std::thread(&ClientSocket::threadRecvData, this);
						_bRecvThread = true;
					}

					if (!_bLogicWorkerThread)
					{
						// 逻辑消息的工人线程，专门处理 _dequeRecvMsg 消息
						_logicWorkerThread = std::thread(&ClientSocket::threadLogicWork, this);
						_bLogicWorkerThread = true;
					}

					if (!_bSendThread)
					{
						// 发送线程
						_sendThread = std::thread(&ClientSocket::threadSendData, this);
						_bSendThread = true;
					}
				}
				else
				{
					// 暂时用睡眠跑着，以后要换成消息事件 ???
					std::this_thread::sleep_for(std::chrono::milliseconds(_autoReConnectTime));
				}
			}
			continue;
		}
		else if (CORE_SOCKET_CLOSE == tmp)	// 断线
		{
			log("threadCoreWork CORE_SOCKET_CLOSE");

			// 等待接收线程结束
			if (_recvThread.joinable())
			{
				_recvThread.join();
				_bRecvThread = false;
			}

			//pGnsClientFrame->OnClose(_clientId);
			pLogic->OnClose(_clientId);

			_curRetryCount = 0;					// 当前重连次数

			if (_autoReConnect)
			{
				// 暂时用睡眠跑着，以后要换成消息事件 ???
				std::this_thread::sleep_for(std::chrono::milliseconds(_autoReConnectTime));

				std::lock_guard<std::mutex> guard(_mutexCoreMsg);	// 加锁
				_dequeCoreMsg.push_back(CORE_SOCKET_RECONNECT);

				continue;
			}
		}  // 解锁，守护的析构会解除
		else if (CORE_SOCKET_RECONNECT == tmp)		// 断线后重新连接
		{
			// 非连接 &&  断线后自动重连  &&  当前重连次数未超限
			if (!m_bReconnect.load() && _autoReConnect && _curRetryCount <= _retryConnectMaxCount)		// 断线后自动重连
			{
				if (reconnectAfterOffline())
				{
					if (!_bRecvThread)
					{
						// 接收线程
						_recvThread = std::thread(&ClientSocket::threadRecvData, this);
						_bRecvThread = true;
					}

					if (!_bLogicWorkerThread)
					{
						// 逻辑消息的工人线程，专门处理 _dequeRecvMsg 消息
						_logicWorkerThread = std::thread(&ClientSocket::threadLogicWork, this);
						_bLogicWorkerThread = true;
					}

					if (!_bSendThread)
					{
						// 发送线程
						_sendThread = std::thread(&ClientSocket::threadSendData, this);
						_bSendThread = true;
					}
				}
				else
				{
					// 暂时用睡眠跑着，以后要换成消息事件 ???
					std::this_thread::sleep_for(std::chrono::milliseconds(_autoReConnectTime));
				}
			}
			continue;
		}
		else if (CORE_SOCKET_NO_HEATBEAT == tmp)	// 无心跳
		{
			continue;
		}

		
	}
}

/* --------------------------------------------------------------------------
函数说明：制做成原子包，将 n 多个小包合成一个完整的包
传入参数：
	pData			收到的数据
    iDataLen		收到的数据长度
返回值：
	成功则返回 0，否则返回错误码参考 SERVER_HANDLE_ERR 枚举：
--------------------------------------------------------------------------*/
int ClientSocket::makeAtomMsgBlock(const char* pData, int iDataLen)
{
	CMemBlockPool2* _pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;

	//int iPoolSize = _pMsgPool->get_current_element_count();		// 测试用

	if (!_pMsgPool)
		return SHE_CMemBlockPool;

	if (iDataLen <= 0 ||  0==pData)
	{
		log("recv data len=%d  or  recv data is null \n", iDataLen);
		return SHE_SmallPackegIsZero;	// 收到包的长度是零
	}

	//--- 合并成一个原子包后，就可以出池一个消息块来放此原子包，此消息块就可以递送了 ------------

	int iPackegLen = iDataLen;
	int iTempStoreBoxLen = m_pStoreBox->length();	// 测试用，存储箱读写头差值长度

	m_pStoreBox->copy(pData, iDataLen);	// 消息进入存储箱

	CORE_MSG_HEAD* pHead = NULL;
	int iVerifyResult = 0;	// 判断包的有效性
	int iCount = 0;

	bool bFirstHead = true;	// 第一个头

	char*	pBody = NULL;	// 包体的长度
	int		iBodyLen = 0;	// 包体
	int		iVerifyFailCount = 0;	// 包效验失败的次数

	do
	{
		pHead = (CORE_MSG_HEAD*)m_pStoreBox->rd_ptr();

		// 判断包的有效性
		iVerifyResult = isPackegValid(m_pStoreBox->rd_ptr(), m_pStoreBox->length());
		if (0 != iVerifyResult)
		{
			if (SHE_PackageNotOver == iVerifyResult)	// 包体还未发过来，就应该等到下一个包完整到达时才判断
				break;	// 退出循环

			iVerifyFailCount++;
			printf("iVerifyResult=%d iCount=%d \n", iVerifyResult, iCount);

			// 找到下一个包头的标志位
			bool bFind = false;
			while (m_pStoreBox->length()>0)
			{
				m_pStoreBox->rd_ptr(1);	// 消息存储箱的“读”位置下移一个字节
				pHead = (CORE_MSG_HEAD*)m_pStoreBox->rd_ptr();
				if (0xaaaaaaaaaaaaaaaa == pHead->_beginFlag)	// 找到包头啦
				{
					bFind = true;
					break;
				}
			}

			if (bFind)	// 找到包头啦
			{
				continue;
			}
			else	// 消息存储箱根本就没有包头
			{
				log("没有包头？？？");
				m_pStoreBox->reset();	// 消息存储箱刷新
				return SHE_Verify_Fail;
			}
		}

		//iPoolSize = _pMsgPool->get_current_element_count();		// 测试用

		// 将原子包发向宿主
		CMemBlock2* pAtomMsg = NULL;

		#if 1
				_pMsgPool->retrieve(&pAtomMsg, "ClientSocket::makeAtomMsgBlock");	// 出池一个原子消息块

				assert(pAtomMsg);
				if (!pAtomMsg)
					return SHE_pAtomMsg_retrieve_Fail;	// 原子包出池失败
		#else
				// 暂时用于测残包的

		#endif

		pAtomMsg->copy(m_pStoreBox->rd_ptr(), pHead->_totalSize);

		if (pAtomMsg->length()>0)
		{
			if (0)
			{	// 本段测试用
				int iMainCode = 0;
				int iSubCode = 0;
				char* pData = NULL;
				int iDataLen = 0;
				_pMsgPool->get_core_msg_data(pAtomMsg, &iMainCode, &iSubCode, pData, &iDataLen);

				log("%s iSubCode=%d \n", "ClientSocket::makeAtomMsgBlock", iSubCode);
			}

			// 在这里校验包头与包体，目前还没有校验...... ???
			CORE_MSG_HEAD* pAtomMsgHead = (CORE_MSG_HEAD*)(pAtomMsg->base());

			// 加入接收到的消息容器，里面的元素已经全是原子包
			std::lock_guard<std::mutex> guard(_mutexRecvMsg);
			_dequeRecvMsg.push_back(pAtomMsg);

			//iPoolSize = _pMsgPool->get_current_element_count();	// 测试用
		}
		else  // 0长度，自行归池，不再投递
		{
			//m_pCoreClient->OnCommError("ClientSocket::makeAtomMsgBlock ，pAtomMsg->length() 的值是 0 \n");

			_pMsgPool->recycle(pAtomMsg, "ClientSocket::makeAtomMsgBlock");	// 归池
		}

		m_pStoreBox->rd_ptr(pHead->_totalSize);

		//} while(pHead->_totalSize > 0 && pHead->_totalSize <= m_iMsgBlockSize  && m_pStoreBox->length() >= pHead->_totalSize) ;	// 这一句引发的问题：对端先发一个1056个字节的大包，再发一个160字节小包，后面的小包收不到
	} while (m_pStoreBox->length() > 0);	// 此句是正确的写法

	if (m_pStoreBox->length() > 0)	// pRecv 内有不完整的包，保留残包
	{
		// 将残余的内容移到头
		m_pStoreBox->remains_move_to_head();
	}
	else	// pRecv 内都是完整的包，且而都已经处理了
	{
		//m_pStoreBox->reset();	// 速度快，但写指针之后是脏数据，不便于查错
		m_pStoreBox->clear();	// 速度慢，但很干净，方便查错
	}

	return 0;
}


/* --------------------------------------------------------------------------
函数说明：判断包的有效性
传入参数：
	pData				要判断的字节流，第一个包肯定位于字节流的最前端。注意：字节流通常会包含很多个包。
	iDataLen			字节流的长度
	iEveryMsgBlockLen	每个消息包的固定长度，其值通常为 512、1024、2048、4096 等很整齐的值
返回值：
	包是有效的则返回 0 ；
	包是无效的则返回错误码，错误码列表如下：
	10	标志位判断失败
	20	包的长度判断失败
--------------------------------------------------------------------------*/
int ClientSocket::isPackegValid(const char* pData, int iDataLen)
{
	if (0 == pData || 0 == iDataLen)
		return false;

	const CORE_MSG_HEAD* pCoreHead = (CORE_MSG_HEAD*)pData;

	if (0xaaaaaaaaaaaaaaaa != pCoreHead->_beginFlag)	// 标志位判断
	{
		return SHE_CoreHeadFlagFail;	// 标志位判断失败
	}

	// 包头若过来了，但包体还未发过来，就应该等到下一个包完整到达时才判断
	if (pCoreHead->_totalSize > iDataLen)
		return SHE_PackageNotOver;

	if (pCoreHead->_totalSize <= 0)
	{
		char szBuf[256] = { 0 };
		sprintf(szBuf, "错误包，%s pCoreHead->_totalSize=%d", "ClientSocket::isPackegValid", pCoreHead->_totalSize);
		log("%s", szBuf);

		return SHE_pCoreHead_iTotalSize_is_0;
	}

	//--- 长度判断 -----------------
	if (pCoreHead->_totalSize > 0 && pCoreHead->_totalSize <= _msgBlockSize)
	{
	}
	else
	{
		return SHE_PackageLenFail;	// 包的长度判断失败
	}

	// 加密层判断，待做 ???

	return 0;
};

/* --------------------------------------------------------------------------
函数说明：发送数据
传入参数：
	iMainCode			主码
	iSubCode			辅助码
	pData					要发送的数据
	iDataLen				要发送的数据长度
	szFunName		调用本指令时所在函数的名称，方便查错
返回值：
	成功则返回 0，失败返回错误码
--------------------------------------------------------------------------*/
int ClientSocket::sendMsg(int iMainCode, int iSubCode, char* pData, int iDataLen, char* szFunName)
{
	if (!m_bStart.load())	// 不能用 isValid() 作判断，因为断网的时候还可以将消息打入发送队列，网络一回复则可以将队列中的消息发出去
		return SHE_NOT_START;

	CMemBlockPool2* _pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;
	if (!_pMsgPool)
		return 20;

	CMemBlock2* pMemBlock = NULL;	// 消息块出池，记得归池

	if (szFunName)
		_pMsgPool->retrieve_with_content_no_channel(szFunName, pMemBlock, iMainCode, iSubCode, pData, iDataLen);
	else
		_pMsgPool->retrieve_with_content_no_channel("ClientSocket::sendMsg", pMemBlock, iMainCode, iSubCode, pData, iDataLen);

	if (!pMemBlock)
		return SHE_MemBlock_IsNull;

	std::lock_guard<std::mutex> guard(_mutexSendMsg);	// 加锁
	_dequeSendMsg.push_back(pMemBlock);

	return 0;
}

/* --------------------------------------------------------------------------
函数说明：发送数据
传入参数：
	pMemBlock	要发送的消息块，由框架归池，无需手工归池
返回值：
	成功则返回 0，失败返回错误码
--------------------------------------------------------------------------*/
int ClientSocket::sendMsg(CMemBlock2* pMemBlock)
{
	if (!pMemBlock)
		return SHE_MemBlock_IsNull;

	std::lock_guard<std::mutex> guard(_mutexSendMsg);	// 加锁
	_dequeSendMsg.push_back(pMemBlock);

	return 0;
}

// 直实发送数据，由本函数归池
int ClientSocket::realSendMsg(CMemBlock2* pMemBlock)
{
	if (!isValid())
		return SHE_NOT_isValid;

	CMemBlockPool2* _pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;
	if (!_pMsgPool)
		return SHE_CMemBlockPool;

	int iSuccCount = 0;			// 发送成功的字节数

	if (pMemBlock && pMemBlock->length() > 0)
	{
		// 若 iSuccCount==-1 或 小于 pMemBlock->length() 则表示发送失败
		iSuccCount = _socket.Send(pMemBlock->base(), pMemBlock->length(), 0);

		if (iSuccCount == pMemBlock->length())	// 发送成功
		{
			_pMsgPool->recycle(pMemBlock, "ClientSocket::sendHeartbeatPackeg");	// 发送成功必需归池
		}
		else  // 发送失败，需要将发送消息重新放回发送消息队列的头部
		{
			_dequeSendMsg.push_front(pMemBlock);	// 注意，是插入到头而不是尾
		}
	}

	return iSuccCount;
}

// 接收到的消息容器的原子消息数量
int ClientSocket::getDequeAtomMsgSize()
{
	std::lock_guard<std::mutex> guard(_mutexRecvMsg);

	int iSize = (int)_dequeRecvMsg.size();
	return iSize;

}

/* --------------------------------------------------------------------------
函数说明：根据客户端参数类型获得客户端参数值
传入参数：
	cpt  见  CLIENT_PARAM_TYPE
返回值：
	成功返回正数值，失败返回 -1
-------------------------------------------------------------------------- */
int ClientSocket::getParamByType(CLIENT_PARAM_TYPE cpt)
{
	switch (cpt)
	{
		case CPT_autoReConnectTime:
			return _autoReConnectTime;

		case CPT_retryConnectMaxCount:
			return  _retryConnectMaxCount;

		case CPT_curRetryCount:
			return  _curRetryCount;

		case CPT_sendHeartbeatElapse:
				return _sendHeartbeatElapse;
	}

	return -1;		// 负数为无效值
}